#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "predicate.h"

struct symbol *symbol_table = NULL;

static unsigned symbol_hash(char *name) {
    unsigned int hash = 0;
    unsigned c;

    while (c = *name++) {
	hash = hash * 9 ^ c;
    }

    return hash;
}

struct symbol *add_symbol(char *str, int i) {
    struct symbol *sym;
    sym = malloc(sizeof(struct symbol));
    sym -> value = i;
    sym -> name = str;
    HASH_ADD_KEYPTR(hh, symbol_table, sym -> name, strlen(sym -> name), sym);

    HASH_FIND_STR(symbol_table, str, sym);
    return sym;
}

struct symbol *get_symbol(char *str) {
    struct symbol *sym;
    HASH_FIND_STR(symbol_table, str, sym);
    return sym;
}

struct astree *init_number(int i) {
    struct numeric *num = malloc(sizeof(struct numeric));

    num -> nodetype = 'n';
    num -> value = i;

    struct astree *ast;
    ast = (struct astree *) num;
    return ast;
}

struct astree *build_ast(struct astree *left, struct astree *right, int nodetype) {
    struct astree *ast = malloc(sizeof(struct astree));

    ast -> left = left;
    ast -> right = right;
    ast -> nodetype = nodetype;

    return ast;
}

struct astree *get_ast(struct symbol *sym) {
    int number = sym -> value;
    return init_number(number);
}

int evaluate_ast(struct astree *ast) {
    int nodetype = ast -> nodetype;
    struct numeric *num;
    int result, leftast, rightast;
    
    switch (nodetype) {
    case 'n':
	num = (struct numeric *) ast;
	result = num -> value;
	break;
    case 'u':
	num = (struct numeric *) ast;
	result = -(num -> value);
	break;
    case '+':
	result = evaluate_ast(ast -> left) + evaluate_ast(ast -> right);
	break;
    case '-':
	result = evaluate_ast(ast -> left) - evaluate_ast(ast -> right);
	break;
    case '*':
	result = evaluate_ast(ast -> left) * evaluate_ast(ast -> right);
	break;
    case '/':
	result = evaluate_ast(ast -> left) / evaluate_ast(ast -> right);
	break;
    case '%':
	leftast = evaluate_ast(ast -> left);
	rightast = evaluate_ast(ast -> right);
	if (leftast < rightast) {
	    result = leftast;
	} else if (leftast > rightast) {
	    result = leftast - rightast;
	    while (result >= rightast) {
		result -= rightast;
	    }
	} else {
	    result = 0;
	}
	break;
    }

    return result;
}

char *substring(char *string, int length) {
    char *substr;
    substr = strndup(string, length);
    return substr;
}

struct astree *symbol_to_expression(struct varname *vn) {
    char *variable = substring(vn -> name, vn -> length);
    struct astree *ast;
    
    struct symbol *sym = get_symbol(variable);
    if (!sym) {
	ast = init_number(0);
	return ast;
    }
    ast = get_ast(sym);
    return ast;
}

void assignment(struct varname *vn, struct astree *ast) {
    add_symbol(substring(vn -> name, vn -> length), evaluate_ast(ast));
}

struct astree *function_call(struct varname *function, struct arguments *arg) {

}

void define_function(struct varname *function_name, struct varname *argument, struct astree *statements) {

}

struct statement_list *store_expression(struct astree *ast) {
    struct statement_list *sl = malloc(sizeof(struct statement_list));
    sl -> exp = ast;
    sl -> next = NULL;
    return sl;
}

struct arguments *bind_arguments(struct varname *vn, struct arguments *ar) {

}

bool init_condition(struct astree *first, int logical_type, struct astree *second) {
    bool con;
    switch (logical_type) {
    case 0:
	if (evaluate_ast(first) == evaluate_ast(second)) {
	    con = true;
	} else {
	    con = false;
	}
	break;
    case 1:
	if (evaluate_ast(first) > evaluate_ast(second)) {
	    con = true;
	} else {
	    con = false;
	}
	break;
    }
    return con;
}

struct astree *negate_ast(struct astree *ast) {
    ast -> nodetype = 'u';
    return ast;
}

struct astree *evaluate_condition(bool con, struct astree *if_statement, struct astree *else_statement) {
    if (con) {
	return if_statement;
    } else {
	return else_statement;
    }
}

void yyerror(char *s) {
    fprintf(stderr, "%d: error:\n", yylineno);
}

int main(int argc, char **argv) {
    if (argc > 1) {
	if (!(yyin = fopen(argv[1], "r"))) {
	    perror(argv[1]);
	    return 1;
	}
    }
    return yyparse();
}

